﻿using ProtoBuf;
using System;
using System.Collections.Generic;

namespace Test
{
    [ProtoContract]
    public class DynamicData<TEntity, TKey>
    {
        [ProtoMember(1)]
        public List<TEntity> ListRoom;

        [ProtoMember(2)]
        public Dictionary<TKey, int> DicRoomMapList;
    }

    [ProtoContract]
    public interface IDynamicDataV2Proto<T>
    {
        [ProtoMember(1)]
        IList<T> EntityList { get; set; }
        [ProtoMember(2)]
        DateTime StartDate { get; set; }
        [ProtoMember(3)]
        IList<uint> EntityDateMapList { get; set; }
    }

    [ProtoContract]
    public class DynamicDataV2<T>
    {
        [ProtoMember(1)]
        public List<T> EntityList { get; set; }

        [ProtoMember(2)]
        public DateTime StartDate { get; set; }

        [ProtoMember(3)]
        public List<uint> EntityDateMapList { get; set; }

        private const uint RoomIndexMask = 0x0000FFFF;
        private const uint DateOffsetMask = 0xFFFF0000;

        public static uint CreateRoomMapInfo(ushort roomIndex, ushort dataOffset)
        {
            return SetDateOffset(dataOffset, SetRoomIndex(roomIndex, 0));
        }

        public static ushort GetRoomIndex(uint data)
        {
            return (ushort)(data & RoomIndexMask);
        }

        public static ushort GetDateOffset(uint data)
        {
            return (ushort)((data & DateOffsetMask) >> 16);
        }

        public static uint SetRoomIndex(ushort data, uint source)
        {
            return source & DateOffsetMask | data;
        }

        public static uint SetDateOffset(ushort data, uint source)
        {
            return source & RoomIndexMask | ((uint)data << 16);
        }
    }

    [ProtoContract]
    [Serializable]
    public class DynamicDataV2ForMultiDay<T>
    {
        [ProtoMember(1)]
        public IList<T> EntityList { get; set; }

        [ProtoMember(2)]
        public DateTime StartDate { get; set; }

        [ProtoMember(3)]
        public IList<uint> EntityDateMapList { get; set; }

        public void Add(T entity, ushort dataOffset, uint dayNum)
        {
            if (EntityList == null)
            {
                EntityList = new List<T>();
                EntityList.Add(entity);
                EntityDateMapList = new List<uint>();
                EntityDateMapList.Add(CreateRoomMapInfo(0, dataOffset, dayNum));
                return;
            }
            for (var i = 0; i < EntityList.Count; i++)
            {
                if (EntityList[i].Equals(entity))
                {
                    EntityDateMapList.Add(CreateRoomMapInfo((ushort)i, dataOffset, dayNum));
                    return;
                }
            }
            EntityList.Add(entity);
            EntityDateMapList.Add(CreateRoomMapInfo((ushort)(EntityList.Count - 1), dataOffset, dayNum));
        }

        private const uint EntityIndexMask = 0x0000FFFF;
        private const uint DateOffsetMask = 0x07FF0000;
        private const uint DayNumMask = 0xF8000000;
        private const short MaxDayNum = 31;

        public static uint CreateRoomMapInfo(ushort entityIndex, ushort dataOffset, uint dayNum)
        {
            return SetDayNum(dayNum, SetDateOffset(dataOffset, SetRoomIndex(entityIndex, 0)));
        }

        public static ushort GetRoomIndex(uint data)
        {
            return (ushort)(data & EntityIndexMask);
        }

        public static ushort GetDateOffset(uint data)
        {
            return (ushort)((data & DateOffsetMask) >> 16);
        }

        public static ushort GetDayNum(uint data)
        {
            return (ushort)((data & DayNumMask) >> 27);
        }

        public static uint SetRoomIndex(ushort data, uint source)
        {
            return source & ~EntityIndexMask | data;
        }

        public static uint SetDateOffset(ushort data, uint source)
        {
            return source & ~DateOffsetMask | ((uint)data << 16);
        }

        public static uint SetDayNum(uint dayNum, uint source)
        {
            if (dayNum > MaxDayNum)
            {
                throw new Exception("DayNum超过最大值 : " + dayNum);
            }
            return source & ~DayNumMask | (dayNum << 27);
        }

    }

    [ProtoContract]
    [Serializable]
    public class DynamicDataV2ForMultiDayV2<T>
    {
        [ProtoMember(1)]
        public IList<T> EntityList { get; set; }

        [ProtoMember(2)]
        public DateTime StartDate { get; set; }

        [ProtoMember(3)]
        public IList<uint> EntityDateMapList { get; set; }

        public void Add(T entity, ushort dataOffset, uint dayNum, bool status)
        {
            if (EntityList == null)
            {
                EntityList = new List<T>();
                EntityList.Add(entity);
                EntityDateMapList = new List<uint>();
                EntityDateMapList.Add(CreateRoomMapInfo(0, dataOffset, dayNum, status));
                return;
            }
            for (var i = 0; i < EntityList.Count; i++)
            {
                if (dayNum > 1)
                {
                    if (entity.Equals(EntityList[i]))
                    {
                        EntityDateMapList.Add(CreateRoomMapInfo((ushort)i, dataOffset, dayNum, status));
                        return;
                    }
                }
                else
                {
                    if (EntityList[i].Equals(entity))
                    {
                        EntityDateMapList.Add(CreateRoomMapInfo((ushort)i, dataOffset, dayNum, status));
                        return;
                    }
                }
            }
            EntityList.Add(entity);
            EntityDateMapList.Add(CreateRoomMapInfo((ushort)(EntityList.Count - 1), dataOffset, dayNum, status));
        }

        private const uint EntityIndexMask = 0x00007FFF;
        private const uint StatusMask = 0x00008000;
        private const uint DateOffsetMask = 0x07FF0000;
        private const uint DayNumMask = 0xF8000000;
        private const short MaxDayNum = 31;

        public static uint CreateRoomMapInfo(ushort entityIndex, ushort dataOffset, uint dayNum, bool status)
        {
            var value = SetDayNum(dayNum, SetDateOffset(dataOffset, SetRoomIndex(entityIndex, 0)));
            if (status)
            {
                return SetStatus(1, value);
            }
            return value;
        }

        public static ushort GetRoomIndex(uint data)
        {
            return (ushort)(data & EntityIndexMask);
        }

        public static ushort GetDateOffset(uint data)
        {
            return (ushort)((data & DateOffsetMask) >> 16);
        }

        public static ushort GetDayNum(uint data)
        {
            return (ushort)((data & DayNumMask) >> 27);
        }

        public static ushort GetStatus(uint data)
        {
            return (ushort)((data & StatusMask) >> 15);
        }

        public static uint SetStatus(ushort data, uint source)
        {
            return source & ~StatusMask | (ushort)((data & 1) << 15);
        }

        public static uint SetRoomIndex(ushort data, uint source)
        {
            return source & ~EntityIndexMask | data;
        }

        public static uint SetDateOffset(ushort data, uint source)
        {
            return source & ~DateOffsetMask | ((uint)data << 16);
        }

        public static uint SetDayNum(uint dayNum, uint source)
        {
            if (dayNum > MaxDayNum)
            {
                throw new Exception("DayNum超过最大值 : " + dayNum);
            }
            return source & ~DayNumMask | (dayNum << 27);
        }

    }

    [ProtoContract]
    [Serializable]
    public class DynamicDataV2ForMultiDayV3<T>
    {
        [ProtoMember(1)]
        public IList<T> EntityList { get; set; }

        [ProtoMember(2)]
        public DateTime StartDate { get; set; }

        [ProtoMember(3)]
        public IList<uint> EntityDateMapList { get; set; }

        private readonly object _addLocker;

        public DynamicDataV2ForMultiDayV3(bool needCreateLocker = false)
        {
            if (needCreateLocker)
            {
                _addLocker = new object();
            }
        }

        public DynamicDataV2ForMultiDayV3() { }

        private void AddInternal(T entity, ushort dataOffset, uint dayNum)
        {
            if (EntityList == null)
            {
                EntityList = new List<T>();
                EntityList.Add(entity);
                EntityDateMapList = new List<uint>();
                EntityDateMapList.Add(CreateRoomMapInfo(0, dataOffset, dayNum));
                return;
            }
            for (var i = 0; i < EntityList.Count; i++)
            {
                if (dayNum > 1)
                {
                    if (entity.Equals(EntityList[i]))
                    {
                        EntityDateMapList.Add(CreateRoomMapInfo((ushort)i, dataOffset, dayNum));
                        return;
                    }
                }
                else
                {
                    if (EntityList[i].Equals(entity))
                    {
                        EntityDateMapList.Add(CreateRoomMapInfo((ushort)i, dataOffset, dayNum));
                        return;
                    }
                }
            }
            EntityList.Add(entity);
            EntityDateMapList.Add(CreateRoomMapInfo((ushort)(EntityList.Count - 1), dataOffset, dayNum));
        }

        public void Add(T entity, ushort dataOffset, uint dayNum)
        {
            if (_addLocker != null)
            {
                lock (_addLocker)
                {
                    AddInternal(entity, dataOffset, dayNum);
                }
            }
            else
            {
                AddInternal(entity, dataOffset, dayNum);
            }
        }

        private const uint EntityIndexMask = 0x00007FFF;
        private const uint DateOffsetMask = 0x07FF0000;
        private const uint DayNumMask = 0xF8000000;
        private const short MaxDayNum = 31;

        public static uint CreateRoomMapInfo(ushort entityIndex, ushort dataOffset, uint dayNum)
        {
            return SetDayNum(dayNum, SetDateOffset(dataOffset, SetRoomIndex(entityIndex, 0))); ;
        }

        public static ushort GetRoomIndex(uint data)
        {
            return (ushort)(data & EntityIndexMask);
        }

        public static ushort GetDateOffset(uint data)
        {
            return (ushort)((data & DateOffsetMask) >> 16);
        }

        public static ushort GetDayNum(uint data)
        {
            return (ushort)((data & DayNumMask) >> 27);
        }

        public static uint SetRoomIndex(ushort data, uint source)
        {
            return source & ~EntityIndexMask | data;
        }

        public static uint SetDateOffset(ushort data, uint source)
        {
            return source & ~DateOffsetMask | ((uint)data << 16);
        }

        public static uint SetDayNum(uint dayNum, uint source)
        {
            if (dayNum > MaxDayNum)
            {
                throw new Exception("DayNum超过最大值 : " + dayNum);
            }
            return source & ~DayNumMask | (dayNum << 27);
        }

    }
}
